import random;
from Activity import Activity;
from HmmSubject import HmmSubject;

class BlackBoard(object):
    def __init__(self, inputSim):
        self.stgm = inputSim.stgm;
        self.graph = self.stgm.Graph();
        self.inputSim = inputSim;
#        self.aStates = inputSim.aStates;
        self.aActs_Time = dict();
        self.aKeys_Time = [];
        self.aOngoingActs = dict();
        self.aNewActsT = [];
        self.aNullNodes = dict();
        self.aMExNodes = dict();
    
#    def getIndex(self, aAct):
#        for i in range(len(self.aStates)): #For each State
#            bMatch = True;
#            if aAct[0] == self.aStates[i][0]: #If has same Activity name
#                if len(aAct[1])!=len(self.aStates[i][1]): #If has Diff # Args
#                    bMatch = False;
#                for j in range(len(aAct[1])): #For each Arg element
#                    if ((aAct[1][j]=="*" and self.aStates[i][1][j]!="*") or
#                        (aAct[1][j]!="*" and self.aStates[i][1][j]=="*")):
#                        bMatch = False;
#                if bMatch==True:
#                    return i;
#        return -1;
    
#    def getMatches(self, aActT0, actT1):
#        aMatches = [];
#        for i in range(len(aActT0)): #For each Act in t-1
#            for j in range(len(aActT0[i].aPersons)): #For each Arg t-1 element
#                if aActT0[i].aPersons[j]=="*":
#                    continue;
#                bMatch = False;
#                for k in range(len(actT1[1])): #For each Arg t element
#                    if(aActT0[i].aPersons[j] == actT1[1][k]):
#                        bMatch = True;
#                        break;
#                if bMatch==False:
#                    break;
#            if bMatch==True:
#                aMatches.append([i+1, [aActT0[i].sActivity, aActT0[i].aPersons]]);
#        return aMatches;
#    
#    def getMatch(self, actT0, actT1):
#        for j in range(len(actT0[1])): #For each Arg t-1 element
#            if actT0[1][j]=="*":
#                continue;
#            bMatch = False;
#            for k in range(len(actT1[1])): #For each Arg t element
#                if(actT0[1][j] == actT1[1][k]):
#                    bMatch = True;
#                    break;
#            if bMatch==False:
#                break;
#        return bMatch;
    
    def removeAct(self, sActCode):
        #Find Activity Node (by its given Code)
        aRemActs = [];
        print "\n\n---Activity To Remove:---"
        for sKeyT in self.aKeys_Time:
            for actM in self.aActs_Time[sKeyT]:
                if actM.sId.split(".")[0]==sActCode:
                    aRemActs.append(actM);
                    print actM.rootAct.name;
        #Get InitT & EndT Nodes affected by this Activity
        if aRemActs==[]: return;
        initRemAct, endRemAct = aRemActs[0], aRemActs[-1];
        sT0, sT1 = initRemAct.rootAct.aTimes[0], initRemAct.rootAct.aTimes[-1];
        aTailRemActs, aHeadRemActs = [], [];
        if (int(sT0)>=int(self.aKeys_Time[0])): #If this InitT is NOT before any other Act
            aTailRemActs = self.check_InitEnd_Act(sActCode, sT0, sT1); #Get InitT Nodes
        sT0 = endRemAct.rootAct.aTimes[-1];
        if self.aKeys_Time.index(sT0)+1 < len(self.aKeys_Time): #If this EndT is NOT after any other Act
            sT1 = self.aKeys_Time[self.aKeys_Time.index(sT0)+1];
            aHeadRemActs = self.check_InitEnd_Act(sActCode, sT0, sT1); #Get InitT Nodes  
        print "\nNodes at Tail-Act to Remove:";
        for tailAct in aTailRemActs:
            print "   "+tailAct.rootAct.name;
        print "\nNodes at Head-Act to Remove:";
        for headAct in aHeadRemActs:
            print "   "+headAct.rootAct.name;
        #Modify EndT Range of these other Activities Segments
        print "\nChanging Ranges:"
        sT0 = initRemAct.rootAct.aTimes[0];
        if int(sT0)<int(self.aKeys_Time[0]): #Checking in Tail Acts
            sKeyT = self.aKeys_Time[0]; #Get "t+1"
        elif self.aKeys_Time.index(sT0)+1 < len(self.aKeys_Time):
            sKeyT = self.aKeys_Time[self.aKeys_Time.index(sT0)+1]; #Get "t+1"
        for tailAct in aTailRemActs: #Modify Range in Tail Acts
            sCode = tailAct.sId.split(".")[0];
            for actM in self.aActs_Time[sKeyT]:
                if actM.sId==(sCode+"."+sKeyT):
                    s_T0, s_T1 = tailAct.rootAct.aTimes[0], actM.rootAct.aTimes[-1];
                    print "---Changing Tail range", actM.rootAct.name, " to: ["+s_T0+"-"+s_T1+"]";
                    actM.changeInitT(s_T0);
        sT0 = endRemAct.rootAct.aTimes[-1];
        sKeyT = "";
        if self.aKeys_Time.index(sT0)+1 < len(self.aKeys_Time): #Checking in Head Acts
            sKeyT = self.aKeys_Time[self.aKeys_Time.index(sT0)+1]; #Get "t+1"
        for headAct in aHeadRemActs: #Modify Range in Head Acts
            sCode = headAct.sId.split(".")[0];
            for actM in self.aActs_Time[sKeyT]:
                if actM.sId==(sCode+"."+sKeyT):
                    s_T0, s_T1 = headAct.rootAct.aTimes[0], actM.rootAct.aTimes[-1];
                    print "---Changing Head range", actM.rootAct.name, " to: ["+s_T0+"-"+s_T1+"]";
                    actM.changeInitT(s_T0);
        #Removing All Act nodes of this Activity Code (and all others affected by this)
        aRemActs.extend(aTailRemActs);
        aRemActs.extend(aHeadRemActs);
        aT, aT_Relink = [], [];
        print "\nOld self.aActs_Time:", self.aActs_Time;
        print "Old self.aKeys_Time:", self.aKeys_Time;        
        if self.aOngoingActs.has_key(sActCode): #Removing Act if it's Ongoing
            print "*****", self.aOngoingActs;
            del self.aOngoingActs[sActCode];
            print "*****", self.aOngoingActs;
        for remAct in aRemActs: #Removing All Acts from TimeLine
            sT = remAct.rootAct.aTimes[-1];
            if not(sT in aT):
                aT.append(sT);
            iRemAct = self.aActs_Time[sT].index(remAct);
            del self.aActs_Time[sT][iRemAct];
            print "Removing Act:", remAct.rootAct.name;
            remAct.removeAct(self.graph);
        print aT;
        #Remove Null Nodes
        for sT in aT:
            self.removeNullNodes(sT);
        for sT in aT: #Removing Time-Slots from TimeLine (if NO Act Sgms exist there)
            print "New self.aActs_Time:", self.aActs_Time;
            print "New self.aKeys_Time:", self.aKeys_Time;
            if len(self.aActs_Time[sT])==0:
                print "Removing Time:", sT;
                del self.aActs_Time[sT];
                del self.aKeys_Time[self.aKeys_Time.index(sT)];
            else:
                aT_Relink.append(sT);
        print "New self.aActs_Time:", self.aActs_Time;
        print "New self.aKeys_Time:", self.aKeys_Time;
        #Relink All remaining Act nodes
        if sKeyT!="": aT_Relink.append(sKeyT);
        aT_Relink.sort();
        print "\nRelinking on Times:", aT_Relink;
        for sK in aT_Relink:
            self.linkActivities(sK);
        #Remove Mutual Exclusive Nodes
        for sT in aT:
            self.removeMExNodes(sT);
        self.graph.setup();
            
    def check_InitEnd_Act(self, sActCode, sT0, sT1):
        #Check if there is NOT other Head nodes (in this InitT/EndT)
        for actM in self.aActs_Time[sT0]:
            sId = actM.rootAct.sId.split(".")[0];
            if sId!=sActCode and not(actM.bCopySegment):
                return [];
        #Check if there is NOT other Tail nodes (in this InitT/EndT)
        if sT1=="":
            return [];
        for actM in self.aActs_Time[sT1]:
            sId = actM.rootAct.sId.split(".")[0];
            if sId!=sActCode and not(self.graph.idndict.has_key(sId+"."+sT0)):
                return [];
        #If neither Tails nor Heads were found (in this InitT/EndT), then remove all these Acts
        aRemActs = [];
        for actM in self.aActs_Time[sT0]:
            if actM.rootAct.sId!=(sActCode+"."+sT0):
                aRemActs.append(actM);
        return aRemActs;
    
    def removeNullNodes(self, sTime):
        print "Checking Null Time:", sTime;
        if not(self.aActs_Time.has_key(sTime)):
            return;
        aRemNulls = [];
        aArgs = [];
        for node in self.aActs_Time[sTime]: #Get All Arg Subjects of this "t"
            if node.rootAct.actName=="Null": continue;
            for sArg in node.rootAct.aSubjects:
                if sArg=="*": continue;
                if not(sArg in aArgs):
                    aArgs.append(sArg);
        for node in self.aActs_Time[sTime]: #For each Null Node in this "t"            
            if node.rootAct.actName!="Null": continue;
            if not(node.rootAct.aSubjects[0] in aArgs):
                aRemNulls.append(node);            
        for remAct in aRemNulls: #Removing All Acts from TimeLine
            iRemAct = self.aActs_Time[sTime].index(remAct);
            del self.aActs_Time[sTime][iRemAct];
            print "Removing NullAct:", remAct.rootAct.name;
            remAct.removeAct(self.graph);
        
    def getProbSeq(self, aActCodes):
        aActNodes = [];
#        aEvNodes = [];
        nProbSeq = 1.0;
#        #Set Evidence Nodes
#        for sKeyT in self.aActs_Time:
#            for actM in self.aActs_Time[sKeyT]:
#                for childN in actM.aChildNodes:
#                    self.graph.addobs(childN, True);
#                    aEvNodes.append(childN.sId);
        self.setEvObs();
        self.setMExObs();
        #Get Act Nodes
        for actCode in aActCodes:
            if not(self.graph.idndict.has_key(actCode)):
                print "Activity " + actCode + " was Not found.";
                continue;
            aActNodes.append(self.graph.idndict[actCode]);
        #Get Probability of Sequence
        for actM in aActNodes:
            print self.graph.postprob([actM])[0];
            nProbSeq *= self.graph.postprob([actM])[0];
# #             nProbSeq *= self.graph.postprob([actM])[0];
# #             self.graph.addobs(actM, True);
# #         for actM in aActNodes:
# #             self.graph.removeObs(actM);
#        for i in range(len(aEvCodes)):
#            if not(self.graph.idndict.has_key(aEvCodes[i])):
#                print "Evidence " + aEvCodes[i] + " was Not found.";
#                continue;
#            evCode = self.graph.idndict[aEvCodes[i]];
#            self.graph.addobs(evCode, aEvVals[i]);
        print "aActCodes:", aActCodes;
#        print "aEvNodes:", aEvNodes;
#        print aEvCodes;
#        print aEvVals;
        print "Posts:", self.graph.postprob(aActNodes);
        return nProbSeq;
    
    def setEvObs(self, nMaxT=float("inf")):
        #Set ALL Evidence Nodes
        for sKeyT in self.aActs_Time:
            if float(sKeyT)>nMaxT: continue;
            for actM in self.aActs_Time[sKeyT]:
                for childN in actM.aChildNodes:
                    self.graph.addobs(childN, True);                    
    
    def removeEvObs(self):
        #Set ALL Evidence Nodes
        for sKeyT in self.aActs_Time:
            for actM in self.aActs_Time[sKeyT]:
                for childN in actM.aChildNodes:
                    self.graph.removeObs(childN);
    
    def setMExObs(self, nMinT=0, nMaxT=float("inf")):
        #Set ALL MEx Nodes
        for sKeyT in self.aMExNodes:
            for nodeME in self.aMExNodes[sKeyT]:
                if float(sKeyT)<nMinT or float(sKeyT)>nMaxT: continue;
                print "Adding Evidence:", nodeME.name;
                self.graph.addobs(nodeME, True);                    
    
    def removeMExObs(self):
        #Unset ALL MEx Nodes
        for sKeyT in self.aMExNodes:
            for nodeME in self.aMExNodes[sKeyT]:
                self.graph.removeObs(nodeME);
    
    def getMaxProbSeq2(self, aActSeqs):
        aProbSeqs = [];
        for actSeq in aActSeqs:
            nProbSeq = self.getProbSeq(actSeq);
            aProbSeqs.append(nProbSeq);
        print aProbSeqs;
        nMaxP = max(aProbSeqs);
        iMaxP = aProbSeqs.index(nMaxP);
        return [iMaxP, nMaxP];
    
    def getMaxProbSeq(self, sTimes=[]):
        if sTimes==[]: sTimes = self.aKeys_Time;
        nMaxProbSeq = 1.0;
        aMaxProbSeq = [];
        self.setEvObs();
        for sT in sTimes:
            [iMaxP, nMaxP] = self.getMaxStProb(sT);
            nMaxProbSeq *= nMaxP;
            aMaxProbSeq.append(iMaxP);
# #             #Set Observations of new "t-1"
# #             nActs = len(self.aActs_Time[sT]);
# #             sVals = bin(2**iMaxP)[2:].zfill(nActs);
# #             for i in range(nActs):
# #                 bVal = bool(int(sVals[i]));
# #                 self.graph.addobs(self.aActs_Time[sT][i].rootAct, bVal);
# #         #Clear all Observations
# #         for sT in sTimes:
# #             for actM in self.aActs_Time[sT]:
# #                 self.graph.removeObs(actM.rootAct);
# #         self.removeEvObs();
        print "Max Prob Seq is:";
        for i in range(len(sTimes)):
            iMax = aMaxProbSeq[i];
            sT = self.aKeys_Time[i];
            print sTimes[i] +" - "+ self.aActs_Time[sT][iMax].rootAct.name;
        return [nMaxProbSeq, aMaxProbSeq];
    
    
    def getHmms(self):
        aHmms = [];
        aArgs = [];
        for sT in self.aKeys_Time: #For each Time Step
            for actM in self.aActs_Time[sT]: #For each Activity
                for sArg in actM.rootAct.aSubjects: #For each Arg (in this Act)
                    if not(sArg in aArgs) and sArg!="*":
                        aArgs.append(sArg);
            for sArg in aArgs: #For each Arg Found
                iArgFound = -1;
                for i in range(len(aHmms)): #For each HMM
                    if aHmms[i].sArgSubject == sArg:
                        iArgFound = i;
                        break;
                if iArgFound>=0: #If HMM exists (with this Arg)
                    aHmms[iArgFound].updateHmm(sT, self.aActs_Time, self.graph);
                else:
                    hmm = HmmSubject(sArg);
                    hmm.updateHmm(sT, self.aActs_Time, self.graph);
                    aHmms.append(hmm);
        for hmm in aHmms:
            print "\nHMM: ", hmm.sArgSubject;
            print hmm.aStates;
            print hmm.aTimes;
        
    
    def getMaxStProb(self, sT):
        aProbSt = [];
        nActs = len(self.aActs_Time[sT]);
        for i in range(nActs):
            print self.aActs_Time[sT][i].rootAct.name;
            aProbSt.append(self.graph.postprob([self.aActs_Time[sT][i].rootAct])[0]);
#             sVals = bin(2**i)[2:].zfill(nActs);
#             aProbSt.append(self.getStateProb(sT, sVals));
        print aProbSt;
        nMaxP = max(aProbSt);
        iMaxP = aProbSt.index(nMaxP);
        return [iMaxP, nMaxP];
    
    def getClosest_Time(self, nTime):
        sClosest_Time = "";
        nClosestDist = float("inf");
        for sT in self.aKeys_Time:
            nDist = abs(nTime-int(sT));
            if nDist<nClosestDist:
                nClosestDist = nDist;
                sClosest_Time = sT;
        return sClosest_Time;
    
    def getStatePosts(self, nTime, nMaxT_Ev=float("inf")):
        self.makeUsrSegment(nTime);
        sT = str(nTime);
#         sT = self.getClosest_Time(nTime);
        self.setEvObs(nMaxT_Ev);
        self.setMExObs(float(0), float(sT));
        aNodes = [];
        for actM in self.aActs_Time[sT]:
            aNodes.append(actM.rootAct);
        aPosts = self.graph.postprob(aNodes);
#         self.removeEvObs();
#         self.removeMExObs();
#         self.removeUsrSegment(nTime);
        return [aNodes, aPosts, sT];
    
    def getJointProb(self, nTime, nMaxT_Ev=float("inf")):
        sT = self.getClosest_Time(nTime);
        nNodes = len(self.aActs_Time[sT]);
        aProbs = [];
        aNodes = [];
        for actM in self.aActs_Time[sT]:
            aNodes.append(actM.rootAct);
        for i in range(2**nNodes):
            sBin = bin(i)[2:].zfill(nNodes);
            nProb = self.getStateProb(nTime, sBin, nMaxT_Ev);
            aProbs.append(nProb);
        return [aNodes, aProbs, sT];
    
    def getStateProb(self, nTime, sVals, nMaxT_Ev=float("inf")):
        sT = self.getClosest_Time(nTime);
        self.setEvObs(nMaxT_Ev);
        self.setMExObs(float(sT), float(sT));
        nProbSt = 1.0;
        if len(sVals)!=len(self.aActs_Time[sT]):
            print "Error: Number or State Values for time [", sT, "] must be ", len(self.aActs_Time[sT]);
        for i,act in enumerate(self.aActs_Time[sT]):
            actNode = act.rootAct;
            bVal = bool(int(sVals[i]));
            if bVal:
                nProbSt *= self.graph.postprob([actNode])[0];
            else:
                nProbSt *= (1-self.graph.postprob([actNode])[0]);
            self.graph.addobs(actNode, bVal);
        for actM in self.aActs_Time[sT]:
            self.graph.removeObs(actM.rootAct);
        self.removeEvObs();
        self.removeMExObs();
        return nProbSt;
    
    def makeUsrSegment(self, nTime):
        if self.aActs_Time.has_key(str(nTime)): return;
        aTimes = [];
        for sT in self.aKeys_Time:
            aTimes.append(int(sT));
        aTimes.sort();
        sT0, sT1 = "None", "None";
        for actTime in aTimes:
            if actTime>nTime:
                sT0 = str(self.getPastKey(str(actTime)));
                sT1 = str(actTime);
                break;
        if sT0=="None" and sT1=="None": #If at that "t" all is Ongoing
            for sCode in self.aOngoingActs:
                ongoingAct = self.aOngoingActs[sCode][0];
                sInitT = sT0;
                if sInitT=="None": sInitT = "0";
                segmentAct = self.getSegmentAct(ongoingAct, sInitT, nTime); #Add copy of this node
                self.addActToBB(segmentAct);                
        else: #If it's not Ongoing
            for nodeM in self.aActs_Time[sT1]:
                if nodeM.rootAct.actName=="Null": continue;
                sInitT = sT0;
                if sInitT=="None": sInitT = "0";
                segmentAct = self.getSegmentAct(nodeM, sInitT, nTime); #Add copy of this node
                self.addActToBB(segmentAct);
        self.addNodesToGraph(str(nTime));
        if sT0!="None":
            self.linkActivities(sT0);
        self.linkActivities(str(nTime));
        if sT1!="None":
            self.linkActivities(sT1);
        self.addMExNodes(str(nTime));
        self.graph.setup();
        
    def removeUsrSegment(self, nTime):
        aTimes = [];
        for sT in self.aKeys_Time:
            aTimes.append(int(sT));
        aTimes.sort();
        sT0, sT1 = "None", "None";
        for actTime in aTimes:
            if actTime>nTime:
                sT0 = str(nTime); #str(self.getPastKey(str(actTime)));
                sT1 = str(actTime);
                break;        
        if sT0=="None": sT0 = str(nTime);
        for remAct in self.aActs_Time[sT0]: #Removing All Acts from TimeLine
            if remAct.rootAct.actName=="Null": continue;
            iRemAct = self.aActs_Time[sT0].index(remAct);
            del self.aActs_Time[sT0][iRemAct];
            print "Removing Act:", remAct.rootAct.name;
            remAct.removeAct(self.graph);
        #Remove Null Nodes
        self.removeNullNodes(sT0);
        #Removing Time-Slots from TimeLine (if NO Act Sgms exist there)
        if len(self.aActs_Time[sT0])==0:
            print "Removing Time:", sT0;
            del self.aActs_Time[sT0];
            del self.aKeys_Time[self.aKeys_Time.index(sT0)];
        #Relink All remaining Act nodes
        if sT1!="None":
            self.linkActivities(sT1);
        #Remove Mutual Exclusive Nodes
        self.removeMExNodes(sT0);
        self.graph.setup();
            
            
    
    def getSegmentAct(self, actModel, nInitT, nEndT):
        nInitT = int(nInitT); nEndT = int(nEndT);
        #Activity:
        newAct = self.createActNode(None, actModel.rootAct, nInitT, nEndT, bRoot=True);
        #Create SubActivities:
        for childNode in actModel.aChildNodes:
            #SubActivity:
            newAct = self.createActNode(newAct, childNode, nInitT, nEndT, bRoot=False);
        print "---Created Segment Activity Node:";
        self.inputSim.printActNode(newAct);
        return newAct;
    
    #Create a new Segment copy from this existing actNode Activity from times nInitT to nEndT 
    def createActNode(self, actModel, actNode, nInitT, nEndT, bRoot=False):
        sActName = actNode.actName;
        sId = actNode.sId.split(".")[0] + "." + str(nEndT);
        aSubjects = []; #Only Actors observed in this Time Event
        aActorsAll = list(actNode.aActorsAll); #All Actors seen so far in this Event
        aActorTypes = list(actNode.aActorTypes); #All Actor Types
        aActorTimes = []; #All Actor Times
        aActorRoles = list(actNode.aActorRoles); #All Actor Roles
        aActorsInfo = []; #All Actor Info (ActorsAll, Types, Times, Roles)
        if bRoot: #If we are creating the root of this new Segment, then check its corresponding Args (for this time interval)
            for i,actorT in enumerate(actNode.aActorTimes):
                aActorTimes.append(list(actorT));
                nActorInit = int(actorT[0].replace("+",""));
                nActorEnd = int(actorT[1].replace("+",""));
                if nEndT>=nActorInit and nEndT<=nActorEnd:
                    aSubjects.append(aActorsAll[i]); #Add this Actor if it falls in actNode.aTimes
        else:
            #If this is Sub-Activity, just copy all its Subjects and ObjectTimes 
            aSubjects = list(actNode.aSubjects);
            for i,actorT in enumerate(actNode.aActorTimes):
                aActorTimes.append(list(actorT));
        nRealInitT = actNode.nRealInitT;
        aTimes = list(actNode.aTimes);
        aTimes[0] = str(nInitT);
        aTimes[-1] = str(nEndT);
        sParentsId = [];
        if bRoot==False:
            for parentId in actNode.sParentsId:
                pId = parentId.split(".")[0] + "." + str(nEndT);
                sParentsId.append(pId);
#         aScores = self.getScore(actNode.sId.split(".")[0], nEndT);
        aScores = list(actNode.aScores);
        sLocation = actNode.location;
        aActorsInfo.append(aActorsAll);
        aActorsInfo.append(aActorTypes);
        aActorsInfo.append(aActorTimes);
        aActorsInfo.append(aActorRoles);
        #Add the Activity or Sub-Activity
        if bRoot:
            actModel = Activity(self.stgm, sActName, aSubjects, nEndT, sId, True);
        actModel.makeXmlNode(sId, sActName, aSubjects, aActorsInfo, aTimes, 
                             nRealInitT, sParentsId, aScores, sLocation, bRoot);
        return actModel;
    
#     #Create a new Segment copy from this existing actNode Activity from times nInitT to nEndT 
#     def createActNode(self, actModel, actNode, nInitT, nEndT, bRoot=False):
#         sActName = actNode.actName;
#         sId = actNode.sId.split(".")[0] + "." + str(nEndT);
#         aObjects = list(actNode.aObjects); #Each corresponds to their respective time in aObjectTimes
#         aObjectTypes = list(actNode.aObjectTypes);
#         aInd_Objects = list(actNode.aInd_Objects); #Each corresponds to their respective time in aObjectTimes
#         aInd_ObjectTypes = list(actNode.aInd_ObjectTypes);
#         if bRoot: #If we are creating the root of this new Segment, then check its corresponding Args (for this time interval)
#             aObjectTimes = [];
#             aSubjects = [actNode.aSubjects[0]] if len(actNode.aSubjectTypes)>0 else []; #Copy same main subject
#             aSubjectTypes = [actNode.aSubjectTypes[0]] if len(actNode.aSubjectTypes)>0 else []; #Copy same main subject
#             for i in range(len(actNode.aObjectTimes)): #Check which other Objects to add for this time segment
#                 aObjT = actNode.aObjectTimes[i];
#                 aObjectTimes.append(list(aObjT));
#                 nObjInit = int(aObjT[0].replace("+",""));
#                 nObjEnd = int(aObjT[1].replace("+",""));
#                 if nEndT>=nObjInit and nEndT<=nObjEnd:
#                     aSubjects.append(aObjects[i]); #Add this Object (as another Subject) if it falls in actNode.aObjectTimes
#                     aSubjectTypes = [actNode.aSubjectTypes[0]] if len(actNode.aSubjectTypes)>0 else []; #Copy same main subject
#             aInd_ObjectTimes = [];
#             for i in range(len(actNode.aInd_ObjectTimes)): #Check which other Ind_Objects to add for this time segment
#                 aInd_ObjT = actNode.aInd_ObjectTimes[i];
#                 aInd_ObjectTimes.append(list(aObjT));
#                 nInd_ObjInit = int(aInd_ObjT[0].replace("+",""));
#                 nInd_ObjEnd = int(aInd_ObjT[1].replace("+",""));
#                 if nEndT>=nInd_ObjInit and nEndT<=nInd_ObjEnd:
#                     aSubjects.append(aInd_Objects[i]); #Add this Ind_Object (as another Subject) if it falls in actNode.aInd_ObjectTimes
#                     aSubjectTypes = [actNode.aInd_ObjectTypes[0]] if len(actNode.aInd_ObjectTypes)>0 else []; #Copy same main subject
#         else:
#             #If this is Sub-Activity, just copy all its Subjects and ObjectTimes 
#             aSubjects = list(actNode.aSubjects);
#             aSubjectTypes = list(actNode.aSubjectTypes);
#             aObjectTimes = list(actNode.aObjectTimes);
#             aInd_ObjectTimes = list(actNode.aInd_ObjectTimes);
#         nRealInitT = actNode.nRealInitT;
#         aTimes = list(actNode.aTimes);
#         aTimes[0] = str(nInitT);
#         aTimes[-1] = str(nEndT);
#         sParentsId = [];
#         if bRoot==False:
#             for parentId in actNode.sParentsId:
#                 pId = parentId.split(".")[0] + "." + str(nEndT);
#                 sParentsId.append(pId);
# #         aScores = self.getScore(actNode.sId.split(".")[0], nEndT);
#         aScores = list(actNode.aScores);
#         sLocation = actNode.location;
#         aOtherArgs = [];
#         aOtherArgs.append(aObjects);
#         aOtherArgs.append(aObjectTimes);
#         aOtherArgs.append(aObjectTypes);
#         aOtherArgs.append(aInd_Objects);
#         aOtherArgs.append(aInd_ObjectTimes);
#         aOtherArgs.append(aInd_ObjectTypes);
#         #Add the Activity or Sub-Activity
#         if bRoot:
#             actModel = Activity(self.stgm, sActName, aSubjects, nEndT, sId, True);
#         actModel.makeXmlNode(sId, sActName, aSubjects, aSubjectTypes, aOtherArgs, aTimes, 
#                              nRealInitT, sParentsId, aScores, sLocation, bRoot);
#         return actModel;
    
    def getScore(self, sCode, nEndT):
        pass;
    
    def addActCollisions(self, actModel, bAddTailSgms=True, bAddHeadSgms=True):
        aTimePts = []; #TimePoints to add Segments
        iAct = self.aKeys_Time.index(str(actModel.nTime)); #Index of Activity in TimeLine
        nInitT = int(actModel.rootAct.aTimes[0]); #Init Time of this Activity
        nEndT = int(self.aKeys_Time[iAct]);
        #Add TimePoints for this Action Segment
        for i in range(iAct+1): #Move from iAct Backwards (up to nInitT)  
            #Note: It also Checks the Init of OtherActs in same Time-Step as this Act
            nEndAct = int(self.aKeys_Time[iAct-i]); #End Time in Each Segment Activity
            if (nEndAct <= nInitT): #If they are NOT overlapping on each other, then NO collision exists
                break;
            for actM in self.aActs_Time[str(nEndAct)]:
                if actM.rootAct.actName=="Null": continue; #Ignore Collisions FROM: Null
                if (nEndAct > nInitT) and (nEndAct < nEndT) and not(nEndAct in aTimePts): 
                    aTimePts.append(nEndAct); #Add Segment's (End) Time Point
                nInitActT = int(actM.rootAct.aTimes[0]);
                if (nInitActT > nInitT) and not(nInitActT in aTimePts):
                    aTimePts.append(nInitActT); #Add Segment's (Init) Time Point
        #Add Collision Segments (from TimePoints Obtained)
        print "-----Adding Segments:---------"
        aTimePts.sort();
        print "aTimePts:", aTimePts;
        if len(aTimePts)>0: #From InitAct to Pt[0]
            nEndPt = aTimePts[0];
            print [nInitT, nEndPt];
            segmentAct = self.getSegmentAct(actModel, nInitT, nEndPt); #Add copy of this node
            self.addActToBB(segmentAct);
        for i in range(len(aTimePts)-1): #From Pt[i] to Pt[i+1] (i>=1)
            nInitPt = aTimePts[i];
            nEndPt = aTimePts[i+1];
            print [nInitPt, nEndPt];
            segmentAct = self.getSegmentAct(actModel, nInitPt, nEndPt); #Add copy of this node
            self.addActToBB(segmentAct);
        self.addFurtherActCollisions(actModel, bAddTailSgms, bAddHeadSgms);
        if len(aTimePts)>0:
            actModel.changeInitT(aTimePts[-1]);
    
    def addFurtherActCollisions(self, actModel, bAddTailSgms=True, bAddHeadSgms=True):
        #Check Further Collisions on Other Activity Nodes (caused by this Activity)
        #---Collect all those Activity Nodes (Collided Activites)---
        aOtherActs_EndP, aOtherActs_InitP = [], [];
        aSgm_EndP, aSgm_InitP = [], [];
        aNames_EndP, aNames_InitP = [], [];
        iAct = self.aKeys_Time.index(str(actModel.nTime)); #Index of Activity in TimeLine
        nInitT = int(actModel.rootAct.aTimes[0]); #Init Time of this Activity
        #Looking for Collisions on Tail Node Activity
        for i in range(len(self.aKeys_Time[:iAct])+1): #Move from HeadAct (iAct) Backwards (up to TailAct+1)
            if (int(self.aKeys_Time[iAct-i]) <= nInitT): break; #If reached TailAct, then Break
            for actM in self.aActs_Time[self.aKeys_Time[iAct-i]]:
                if actM.rootAct.actName=="Null": continue; #Ignore Collisions TO: Null
                nOtherActT0 = int(actM.rootAct.aTimes[0]); 
                if (nOtherActT0 < nInitT):
                    aOtherActs_InitP.append(actM);
                    aSgm_InitP.append(str(nOtherActT0)+"-"+str(nInitT));
                    aNames_InitP.append(actM.rootAct.name);
        #Looking for Collisions on Head Node Activity & (Rest of) Collisions on Tail Node Activity
        #(Check all Segments from iAct+1 [every further segment must End in iAct+1])
        if iAct+1 < len(self.aKeys_Time): #If iAct+1 exists
            for actM in self.aActs_Time[self.aKeys_Time[iAct+1]]: #For each Act in iAct+1
                if actM.rootAct.actName=="Null": continue; #Ignore Collisions to Null
                nOtherActT0 = int(actM.rootAct.aTimes[0]);
                if (nOtherActT0 < actModel.nTime): #Store collisions on Head Act
                    aOtherActs_EndP.append(actM);
                    aSgm_EndP.append(str(nOtherActT0)+"-"+str(actModel.nTime));
                    aNames_EndP.append(actM.rootAct.name);
                if (nOtherActT0 < nInitT): #Store collisions on Tail Act
                    aOtherActs_InitP.append(actM);
                    aSgm_InitP.append(str(nOtherActT0)+"-"+str(nInitT));
                    aNames_InitP.append(actM.rootAct.name);
        #Add Further Collision Segments (from Collided Activities obtained)
        if bAddTailSgms:
            for otherAct_InitP in aOtherActs_InitP:
                nOtherActT0 = int(otherAct_InitP.rootAct.aTimes[0]);
                segmentAct = self.getSegmentAct(otherAct_InitP, nOtherActT0, nInitT); #Add copy of this node
                self.addActToBB(segmentAct); #Add it to the TimeLine
                otherAct_InitP.changeInitT(nInitT); #Update InitT of Segments (that collided with TailAct)
        if bAddHeadSgms:
            for otherAct_EndP in aOtherActs_EndP:
                nOtherActT0 = int(otherAct_EndP.rootAct.aTimes[0]);
                if (nOtherActT0 < nInitT and bAddTailSgms): nOtherActT0 = nInitT; #Fix Init Time of NEW segment to TailAct (if was before that & TailAct was added)
                segmentAct = self.getSegmentAct(otherAct_EndP, nOtherActT0, actModel.nTime); #Add copy of this node
                self.addActToBB(segmentAct); #Add it to the TimeLine
                otherAct_EndP.changeInitT(actModel.nTime); #Update InitT of Segments (that collided with HeadAct)
        #Print Results
#        print "---Further Collisions from:", actModel.rootAct.name;
#        print "END-Pt Collisions";
#        print "aOtherActs_EndP (Colliding With These Activities):", aNames_EndP;
#        print "aSgm_EndP (FULL Segment TimePt):", aSgm_EndP;
#        print "bAddTailSgms (Tail Collisions Added?):", bAddTailSgms;
#        print "---"
#        print "INIT-Pt Collisions";
#        print "aOtherActs_InitP (Colliding With These Activities):", aNames_InitP;
#        print "aSgm_InitP (FULL Segment TimePt):", aSgm_InitP;
#        print "bAddHeadSgms (Tail Collisions Added?):", bAddHeadSgms;
#        print "";

    def createNullNode(self, sInitT, sEndT, sSubj):
        sActName = "Null";
        aSubjects = [sSubj];
#         aSubjectTypes = ["-"];
        print "NULL at:", sInitT, sEndT;
        sId = "Null_"+sSubj+"." + sEndT;
        aTimes = [sInitT, sEndT];
        aScores = [0];
        aActorsInfo = [aSubjects];
        nullActModel = Activity(self.stgm, sActName, aSubjects, 1, sId, True);
        nullActModel.makeXmlNode(sId, sActName, aSubjects, aActorsInfo, aTimes, 
                             int(sInitT), [], aScores, "", True);
        return nullActModel;
            
    
    def addActToBB(self, actModel):
        sT = str(actModel.nTime);
        if not(int(sT) in self.aNewActsT): #Bookmark the "t" points with only Newly Modified/Added Activities
            self.aNewActsT.append(int(sT));
        #Add a Null Node
        for sSubj in actModel.rootAct.aSubjects:
            if sSubj=="*": continue;
            bNullFound = False;
            if self.aActs_Time.has_key(sT):
                for actM in self.aActs_Time[sT]:
                    if actM.rootAct.sId=="Null_"+sSubj+"."+sT:
                        bNullFound = True;
            if not(bNullFound):
                nullNode = self.createNullNode("0", sT, sSubj);
                if self.aActs_Time.has_key(sT): #If Existing Time Point
                    self.aActs_Time[sT].append(nullNode);
                else: #If NEW Time Point
                    self.aActs_Time[sT] = [nullNode];
                    self.addKeyTime(sT);
        #Add the given ActModel
        if self.aActs_Time.has_key(sT): #If Existing Time Point
            self.aActs_Time[sT].append(actModel);
        else: #If NEW Time Point
            self.aActs_Time[sT] = [actModel];
            self.addKeyTime(sT);
            
#     def checkOngoingAct(self, actModel):
#         bIsOngoing = actModel.rootAct.aTimes[-1].endswith("+");
#         bWasAdded = self.aOngoingActs.has_key(actModel.sId.split(".")[0]);
#         print "[bIsOngoing, bWasAdded]:", [bIsOngoing, bWasAdded];
#         if bIsOngoing and bWasAdded:
#             self.updateOngoingAct(actModel);
#         elif bIsOngoing and not(bWasAdded):
#             self.addOngoingAct(actModel);
#         elif not(bIsOngoing) and bWasAdded:
#             self.finishOngoingAct(actModel);
#         else:
#             return False;
#         return True;

    def checkOngoingAct(self, actModel):
        bIsOngoing = actModel.rootAct.aTimes[-1].endswith("+");
        bWasAdded = self.aOngoingActs.has_key(actModel.sId.split(".")[0]);
        print "[bIsOngoing, bWasAdded]:", [bIsOngoing, bWasAdded];
        if bIsOngoing and bWasAdded:
            self.updateOngoingAct(actModel);
        elif bIsOngoing and not(bWasAdded):
            self.addOngoingAct(actModel);
        elif not(bIsOngoing) and bWasAdded:
            self.updateOngoingAct(actModel);
            del self.aOngoingActs[actModel.sId.split(".")[0]];
        else:
            return False;
        return True;
        
# #     def updateOngoingAct(self, ongoingAct):
# #         #Try again to Add this ongoingAct (if was Unable bo be added before).
# #         iKey = ongoingAct.sId.split(".")[0];
# #         if self.aOngoingActs[iKey][1]==False: #If hasn't been Added yet, then
# #             self.addOngoingAct(ongoingAct); #Try adding it again
# #             return;
# #         #Update this ongoingAct (by adding another copy in latest Time-Step)
# #         #Note: We assume nEndT is always latest time step (regardless of value given in XML [due to "+"])
# #         self.aOngoingActs[iKey][0] = ongoingAct;
# #         nInitT = self.aOngoingActs[iKey][2]; #Latest Time Added before
# #         nEndT = int(self.aKeys_Time[-1]) if len(self.aKeys_Time)>0 else -1;
# #         if nInitT < nEndT:
# #             self.aOngoingActs[iKey][2] = nEndT;
# #             segmentAct = self.getSegmentAct(ongoingAct, nInitT, nEndT); #Add copy of this node
# #             self.addActToBB(segmentAct);
# #             self.addActCollisions(segmentAct, bAddTailSgms=True, bAddHeadSgms=False); 

    def updateOngoingAct(self, ongoingAct):
        #Update this ongoingAct (by adding another copy in latest Time-Step)
        iKey = ongoingAct.sId.split(".")[0];
        self.aOngoingActs[iKey][0] = ongoingAct;
        nInitT = self.aOngoingActs[iKey][2]; #Latest Time Added before
        nEndT = int(ongoingAct.rootAct.aTimes[-1].replace("+",""));
        ongoingAct.rootAct.aTimes[0] = str(nInitT);
        ongoingAct.rootAct.aTimes[-1] = str(nEndT);
        self.aOngoingActs[iKey][2] = nEndT;
#         if nInitT < nEndT:
#             self.aOngoingActs[iKey][2] = nEndT;
#             segmentAct = self.getSegmentAct(ongoingAct, nInitT, nEndT); #Add copy of this node
#             self.addActToBB(segmentAct);
#             self.addActCollisions(segmentAct, bAddTailSgms=True, bAddHeadSgms=True);
            
    def addOngoingAct(self, ongoingAct):
        bAbleToAdd = True;
        nEndT = int(ongoingAct.rootAct.aTimes[-1].replace("+",""));
        nInitT = int(ongoingAct.rootAct.aTimes[0]);
        ongoingAct.rootAct.aTimes[0] = str(nInitT);
        ongoingAct.rootAct.aTimes[-1] = str(nEndT);
        self.aOngoingActs[ongoingAct.sId.split(".")[0]] = [ongoingAct, bAbleToAdd, nEndT];
        print "self.aOngoingActs", self.aOngoingActs;
#         segmentAct = self.getSegmentAct(ongoingAct, nInitT, nEndT); #Add copy of this node
#         self.addActToBB(segmentAct);
#         self.addActCollisions(segmentAct, bAddTailSgms=True, bAddHeadSgms=True);

    
# #     def addOngoingAct(self, ongoingAct):
# #         nEndT = int(self.aKeys_Time[-1]) if len(self.aKeys_Time)>0 else -1;
# #         bAbleToAdd = len(self.aKeys_Time)>0; #and ongoingAct.nTime<nEndT;
# #         self.aOngoingActs[ongoingAct.sId.split(".")[0]] = [ongoingAct, bAbleToAdd, nEndT];
# #         print "self.aOngoingActs", self.aOngoingActs;
# #         if bAbleToAdd:
# #             nInitT = int(ongoingAct.rootAct.aTimes[0]);
# #             segmentAct = self.getSegmentAct(ongoingAct, nInitT, nEndT); #Add copy of this node
# #             self.addActToBB(segmentAct);
# #             self.addActCollisions(segmentAct, bAddTailSgms=True, bAddHeadSgms=False);
    
#     def finishOngoingAct(self, ongoingAct):
#         #Try again to Add this ongoingAct (if was Unable bo be added before).
#         iKey = ongoingAct.sId.split(".")[0];
#         if self.aOngoingActs[iKey][1]==False: #If hasn't been Added yet, then
#             self.addOngoingAct(ongoingAct); #Try adding it again
#         #Finish this ongoingAct (by adding the Last copy in End Time-Step given)
#         nOngoingEndT = self.aOngoingActs[iKey][2];
#         nInitT = nOngoingEndT if nOngoingEndT!=-1 else int(ongoingAct.rootAct.aTimes[0]); #Latest Time Added before
#         nEndT = int(ongoingAct.rootAct.aTimes[-1]);
#         if nInitT < nEndT:
#             segmentAct = self.getSegmentAct(ongoingAct, nInitT, nEndT); #Add copy of this node
#             self.addActToBB(segmentAct);
#             self.addActCollisions(segmentAct, bAddTailSgms=False, bAddHeadSgms=True);
#         del self.aOngoingActs[ongoingAct.sId.split(".")[0]];
    
    def getNextActsT(self):
        for i in range(len(self.aKeys_Time)-1):
            sKeyT0 = self.aKeys_Time[i];
            sKeyT1 = self.aKeys_Time[i+1];
            if sKeyT0 in self.aNewActsT and not(sKeyT1 in self.aNewActsT):
                self.aNewActsT.append(int(sKeyT1));
        self.aNewActsT.sort();
    
    def addXmlActs_ToBB(self, sXmlFile=""):
        #Read XML file to get Activities
        self.inputSim.makeXmlActivities(sXmlFile);
        #Insert all XML Activities into the timeline of HMM BB
        self.aNewActsT = [];
#         self.nullNode = self.createNullNode("0", "1+");
        for i in range(len(self.inputSim.aActivities)): #For all XML Activities
#             if self.checkOngoingAct(self.inputSim.aActivities[i]): 
#                 continue;
            self.checkOngoingAct(self.inputSim.aActivities[i]);            
            self.addActToBB(self.inputSim.aActivities[i]);
            self.addActCollisions(self.inputSim.aActivities[i]);
#         self.checkOngoingAct(self.nullNode);
        #Add & Link All Activities to the Graph
        print "aKeys_Time:", self.aKeys_Time;
        print "aNewActsT:", self.aNewActsT;
        self.getNextActsT();
        print "aNewActsT:", self.aNewActsT;
        for nT in self.aNewActsT:
            self.addNodesToGraph(str(nT));
            self.linkActivities(str(nT));
            self.addMExNodes(str(nT));
        self.graph.setup();
        
    def addMExNodes(self, sTime):
        for actM in self.aActs_Time[sTime]: #For each Activity in this "t"
            if actM.rootAct.actName=="Null": continue;
            aSubj = [];
            for sArg in actM.rootAct.aSubjects: #For each Arg (in this Act)
                if not(sArg in aSubj) and sArg!="*":
                    aSubj.append(sArg);
        if len(aSubj)==0: return;
        if not(self.aMExNodes.has_key(sTime)):
            self.aMExNodes[sTime] = [];
            for sArg in aSubj:
                newMExNode = self.createMExNode(sTime, sArg); #Create & Update MExNode
                print "Creating MExNode1:", newMExNode.name;
                self.aMExNodes[sTime].append(newMExNode);
        else:
            for sArg in aSubj: #For each Arg
                bUpdated = False;
                for nodeME in self.aMExNodes[sTime]: #For each MExNode
                    if sArg in nodeME.aSubjects: #If MExNode has this sArg
                        print "Updating MExNode:", nodeME.name;
                        self.updateMExNode(nodeME); #Update MExNode
                        bUpdated = True;
                        break;
                if not(bUpdated): #If sArg doesn't have a MExNode, then create one
                    newMExNode = self.createMExNode(sTime, sArg); #Create & Update MExNode
                    print "Creating MExNode2:", newMExNode.name;
                    self.aMExNodes[sTime].append(newMExNode);
    
    def removeMExNodes(self, sTime):
        print "Checking Time:", sTime;
        if not(self.aMExNodes.has_key(sTime)):
            return;
        aRemMExNodes = [];
        for nodeMEx in self.aMExNodes[sTime]: #For each MEx Node in this "t"
            print "Checking MEx:", nodeMEx.name;
            nParents = 0;
            for parentMEx in self.graph.ndict[nodeMEx][0]: #For each MEx's Parent
                if self.graph.ndict.has_key(parentMEx):
                    nParents += 1;
            print "MEx_Parents unlinked:", nParents, "from:", len(self.graph.ndict[nodeMEx][0]), "Name:", nodeMEx.name;
            if nParents==0:
                print "Removing MEx:", nodeMEx.name;
                self.graph.removeNode(nodeMEx);
                aRemMExNodes.append(nodeMEx);
            elif nParents>0:
                self.updateMExNode(nodeMEx);
        for remMEx in aRemMExNodes:
            iNodeMEx = self.aMExNodes[sTime].index(remMEx);
            del self.aMExNodes[sTime][iNodeMEx];        

    def createMExNode(self, sT, sArg):
        sActName = "MEx";
        aSubjects = [sArg];
        sId = "ME_"+sArg+"." + sT;
        aTimes = ["0", sT];
        sSubjects = "(" + ", ".join(aSubjects) + ")";
        sTimes = "[" + "-".join(aTimes) + "]";
        sName = sId+"-"+sActName+"\\n"+sSubjects+"\\n"+sTimes;
        nodeME = self.stgm.Node(sName, sId=sId, actName=sActName, aSubjects=aSubjects, 
                                timeId=int(sT), aTimes=aTimes);
        self.updateMExNode(nodeME);
        return nodeME;
    
    def updateMExNode(self, nodeME):
        sT = nodeME.aTimes[-1];
        sArg = nodeME.aSubjects[0];
        aNodes, aCondProbs = [], [];  #@UnusedVariable
        for actM in self.aActs_Time[sT]:
            if sArg in actM.rootAct.aSubjects: # or actM.rootAct.actName=="Null":
                aNodes.append(actM.rootAct);
        nNodes = len(aNodes);
        aCondProbs = [1.0]*nNodes;
#         aCondProbs = [1.0/float(nNodes)]*nNodes;
        aCPT = self.getCPT(aCondProbs, aNodes);
        self.graph.addnode(nodeME, aNodes, aCPT);
        
                
            
    def addKeyTime(self, sTime):
        for i in range(1,len(self.aKeys_Time)+1):
            if int(sTime) > int(self.aKeys_Time[-i]):                
                k = len(self.aKeys_Time)-i;
                self.aKeys_Time.insert(k+1, sTime);
                return;
        #Else (if sTime < self.aKeys_Time[0])
        self.aKeys_Time.insert(0, sTime);
    
    def addNodesToGraph(self, sTime):
        for i in range(len(self.aActs_Time[sTime])): #For each Act[t]
            aActT1 = self.aActs_Time[sTime][i];
            sAct1 = aActT1.getKeyTexts(); 
            if self.inputSim.bReadXML==True:
                nPrior = self.inputSim.aPriors[self.inputSim.getIndexPrior(sAct1)];
                print "Adding Node Act:", aActT1.sId, aActT1.rootAct.name, nPrior;
                aActT1.makeXmlGraph(self.graph, nPrior);
            else:
                nPrior = self.inputSim.aPriors[self.inputSim.getIndexPrior(sAct1)];
                aActT1.makeRndGraph(self.graph, nPrior);
                
    def getMEx(self, sBinCPT, aActs):
        if sBinCPT.count("1")<2:
            return False;
        #Get All Acts whose CPT entry shows as True
        aActsTrue = [];
        for i, sVal in enumerate(sBinCPT):
#             if sVal=="1" and aActs[i].actName=="Null": #If there are 2 or more True Nodes & one of them is Null, then MEx=True
#                 return True;
            if sVal=="1":
                aActsTrue.append(aActs[i]);
        for actM1 in aActsTrue:
            for actM2 in aActsTrue:
                if actM1!=actM2:
                    for sArg in actM1.aSubjects:
                        if sArg in actM2.aSubjects:
                            return True;
        return False;
            
    def getCPT(self, aCondProbs, aActs):
        aCPT = [];
        nSize = len(aCondProbs);
        for i in range(2**nSize):
            sBinCPT = bin(i)[2:].zfill(nSize); #Get each Binary Number combination
            nProb = 0;
            if not(self.getMEx(sBinCPT, aActs)): #Check if there is Mutual Exclusivity
                #Compute Independence Prob (for this entry in CPT)
                nIndActs, nProb = 0, 1;
                aIndActs = [];
                for j, sVal in enumerate(sBinCPT):
                    if sVal=="1":
                        nProb *= aCondProbs[j];
                        nIndActs += 1;
                        aIndActs.append(j);
                if len(aIndActs)>1:
                    for iAct in aIndActs:
                        print "Independent Act:", aActs[iAct].name;
                    print "---";
                if nIndActs==0:
                    nProb = 0;
            aCPT.append(nProb);
        return aCPT;
            
    def linkActivities(self, sTime):
        for i in range(len(self.aActs_Time[sTime])): #For each Act[t]
            aActT1 = self.aActs_Time[sTime][i];
#             sAct1 = aActT1.getKeyTexts();
            sPastTime = self.getPastKey(sTime);
            bPrior = (True if sPastTime==None else False);
            [aActs, aProbs] = self.getPastLinks(sPastTime, aActT1, bPrior);
#            [aActsM, aProbsM] = self.getMarkovLinks(sTime, sAct1);
#            if len(aActsM)>0:
#                if len(aActs)==0:
#                    aActs = aActsM;
#                    aProbs = aProbsM;                   
#                else:
#                    aActs.extend(aActsM);
#                    aProbs.extend(aProbsM);
            if len(aActs)>=1:
                aProbs = self.getCPT(aProbs, aActs);
                print "Noisy-OR (CPT from "+ aActT1.rootAct.name +"):", aProbs;
#            if len(aActs)==1:
#                print "i:", i, "sTime:", sTime, aActT1.rootAct.name, " - aProbs:", aProbs, " - aActs:", aActs;
#                aProbs = [0.0, aProbs];
            self.graph.addnode(aActT1.rootAct, aActs, aProbs);
#             print "Connections parents:", aActs;
#             for act in aActs:
#                 print "Connection from:", aActT1.rootAct.name, " to:", act.name;
            
    def getPastKey(self, sTime):
        iKey = self.aKeys_Time.index(sTime)-1;
        if iKey<0:
            return None;
        else:
            return self.aKeys_Time[iKey];

    def getPastLinks(self, sT0, actT1, bPrior=False):
        aActs, aProbs = [], [];
        sAct1 = actT1.getKeyTexts();
        nIndexA1_Prior = self.inputSim.getIndexPrior(sAct1);
        nPrior = self.inputSim.aPriors[nIndexA1_Prior];
        if bPrior:
            #Send a Prior
            aProbs.append(nPrior);
            return [aActs, aProbs];
        for j in range(len(self.aActs_Time[sT0])): #For each Act[t-1]
            actT0 = self.aActs_Time[sT0][j];
            sAct0 = actT0.getKeyTexts();
            [nIndexA0, nIndexA1] = self.inputSim.getIndex(sAct0, sAct1);
            sCodeT0, sCodeT1 = actT0.sId.split(".")[0], actT1.sId.split(".")[0];
            initTimeT1, nRealInitT1 = int(actT1.rootAct.aTimes[0]), actT1.rootAct.nRealInitT;
            bCanMakeLink = sCodeT0==sCodeT1 or initTimeT1==nRealInitT1 or actT1.rootAct.actName=="Null";
            if nIndexA0!=None and bCanMakeLink: # and sAct0[0]!="Null":
                #Send a Transition
                aProbs.append(self.inputSim.aTrans[nIndexA0][nIndexA1]);
                aActs.append(actT0.rootAct);
#             elif sAct0[0]==["Null"]:
#                 #Send a Transition (from Null)
#                 nIndexA0 = self.inputSim.getIndexPrior(sAct0);
#                 aProbs.append(self.inputSim.aTrans[nIndexA0][0]);
#                 aActs.append(actT0.rootAct);
        if aActs==[]:
            #Send a Prior
            aProbs.append(nPrior);
        return [aActs, aProbs];

#    def getPastLinks(self, sT0, sAct1, bPrior=False):
#        aActs, aProbs = [], [];
#        nIndexA1 = self.getIndex(sAct1);
#        nPrior = self.inputSim.aPriors[nIndexA1];
#        if bPrior:
#            #Send a Prior
#            aProbs.append(nPrior);
#            return [aActs, aProbs];
#        for j in range(len(self.aActs_Time[sT0])): #For each Act[t-1]
#            actT0 = self.aActs_Time[sT0][j];
#            sAct0 = actT0.getKeyTexts();
#            nIndexA0 = self.getIndex(sAct0);
#            if self.getMatch(sAct0, sAct1):
#                #Send a Transition
#                aProbs.append(self.inputSim.aTrans[nIndexA0][nIndexA1]);
#                aActs.append(actT0.rootAct);
#        if aActs==[]:
#            #Send a Prior
#            aProbs.append(self.inputSim.aPriors[nIndexA1]);
#        return [aActs, aProbs];
    
#    def getMarkovLinks(self, sT0, sAct1):
#        aActs, aProbs = [], [];
#        nIndexA1 = self.getIndex(sAct1);
#        for j in range(len(self.aActs_Time[sT0])): #For each Act[t]
#            actT0 = self.aActs_Time[sT0][j];
#            sAct0 = actT0.getKeyTexts();
#            if sAct0==sAct1:
#                continue;
#            nIndexA0 = self.getIndex(sAct0);
#            if self.getMatch(sAct0, sAct1) and self.getMatch(sAct1, sAct0):
#                #Send Markov Correlation
#                aProbs.append(self.inputSim.aMarkovM[nIndexA0][nIndexA1]);
#                aActs.append(actT0.rootAct);
#        return [aActs, aProbs];
            
    def printGraph(self, bPrior=True):
        self.aAllNodes = self.graph.ndict.keys();
        if bPrior==True:
            aPriors = self.graph.priorprob(self.aAllNodes);
        else:
            aPosts = self.graph.postprob(self.aAllNodes);
        for i in range(len(self.aAllNodes)):
            print "Node " + str(i+1) + ": " + self.aAllNodes[i].name;
            if bPrior==True:
                print "Prior: " + str(aPriors[i]);
            else:
                print "Posterior: " + str(aPosts[i]);                
            for j in range(len(self.graph.ndict[self.aAllNodes[i]][0])):
                print "Parent " + str(j+1) + ": " + self.graph.ndict[self.aAllNodes[i]][0][j].name;
            sCPT_F = "[" + "%.4f" % (1.0-float(self.graph.ndict[self.aAllNodes[i]][1][0]));
            for j in range(1, len(self.graph.ndict[self.aAllNodes[i]][1])):
                sCPT_F += ", %.4f" % (1.0-float(self.graph.ndict[self.aAllNodes[i]][1][j]));
            sCPT_F += "]";
            sCPT_T = "[" + "%.4f" % self.graph.ndict[self.aAllNodes[i]][1][0];
            for j in range(1, len(self.graph.ndict[self.aAllNodes[i]][1])):
                sCPT_T += ", %.4f" % self.graph.ndict[self.aAllNodes[i]][1][j];
            sCPT_T += "]";
            print "CPT-False: " + sCPT_F;
            print "CPT-True:  " + sCPT_T;
            print "";
                
                
                
                    